package com.nulldev.util.web.YouTubeExtractor.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.VariableAPI.MathUtil;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.VariableAPI.util.strings.splitters.Splitters;
import com.nulldev.util.data.Variables;
import com.nulldev.util.data.Arrays.maps.FastMaps;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;

public class YouTubeAmountFormatter {
	private static final Logger log = LoggerFactory.getLogger();

	public static enum MathUtilFormatType {
		THOUSANDS((long) 1e3),
		MILLIONS((long) 1e6),
		BILLIONS((long) 1e9),

		/* Very ugly duplicates to bypass the single enum mapping limit */

		TREAT_AS_STANDARD(1),
		TREAT_AS_STANDARD2(1),
		TREAT_AS_STANDARD3(1),
		TREAT_AS_STANDARD4(1),
		TREAT_AS_STANDARD5(1),
		UNKNOWN(1)

		;

		private final long factor;

		private MathUtilFormatType(final long factor) {
			this(factor, false);
		}

		private MathUtilFormatType(final long factor, final boolean forceStandard) {
			this.factor = factor;
		}

		public long factor() {
			return this.factor;
		}
	}

	private static final Map<String, Map<MathUtilFormatType, String>> FORMAT_MAP = FastMaps.fixedUniMap(new Object[][]
		{
				{ "EN", FastMaps.uniMap(new Object[][]
				{
						{ MathUtilFormatType.THOUSANDS, "K" },
						{ MathUtilFormatType.MILLIONS, "M" },
						{ MathUtilFormatType.BILLIONS, "B" },
						{ MathUtilFormatType.TREAT_AS_STANDARD, "views" },
						{ MathUtilFormatType.TREAT_AS_STANDARD2, "view" },
						{ MathUtilFormatType.TREAT_AS_STANDARD3, "watching" } }) },
				{ "PL", FastMaps.uniMap(new Object[][]
				{
						{ MathUtilFormatType.THOUSANDS, "tys." },
						{ MathUtilFormatType.MILLIONS, "mln" },
						{ MathUtilFormatType.BILLIONS, "mld" },
						{ MathUtilFormatType.TREAT_AS_STANDARD, "subskrybentów" },
						{ MathUtilFormatType.TREAT_AS_STANDARD2, "wyświetlenia" },
						{ MathUtilFormatType.TREAT_AS_STANDARD3, "wyświetleń" },
						{ MathUtilFormatType.TREAT_AS_STANDARD4, "wyświetlenie" },
						{ MathUtilFormatType.TREAT_AS_STANDARD5, "wys" } }) } });

	private static final Pattern PAT_IS_NUM_VALUE = Pattern.compile("[0-9,/,]+");
	private static final Pattern PAT_SPLIT_NUMBER_CHARS = Pattern.compile("[\\d\\.]+|\\D+");

	private static String[] splitNumberAndCharacters(final String payload) {
		final Matcher m = PAT_SPLIT_NUMBER_CHARS.matcher(payload);
		final List<String> output = new ArrayList<String>();

		while (m.find()) {
			output.add(m.group());
		}

		return output.toArray(new String[output.size()]);
	}

	public static boolean isNumber(final String num) {
		return PAT_IS_NUM_VALUE.matcher(Variables.requireNonNullObject(num, "num")).find();
	}

	public static long format(final String _string) {
		if (_string == null || _string.isEmpty())
			return -1;

		final String string = StringsUtil.cleanInvalidCharacters(_string.replace('\u00a0', '\u0020'));
		final String[] text;

		if (!string.contains(" ")) {
			if (StringsUtil.hasOnlyNumbers(string)) {
				return MathUtil.safeParseL(StringsUtil.onlyNumbers(string), -1);
			} else {
				text = splitNumberAndCharacters(string);
			}
		} else {
			text = Splitters.splitString(string, " ");
		}

		final String compare = ArrayUtils.last(text);
		MathUtilFormatType type = MathUtilFormatType.UNKNOWN;
		main_loop: {
			for (final Map<MathUtilFormatType, String> vs : FORMAT_MAP.values()) {
				for (final Map.Entry<MathUtilFormatType, String> es : vs.entrySet()) {
					if (es.getValue().equalsIgnoreCase(compare)) {
						type = es.getKey();
						break main_loop;
					}
				}
			}
		}
		// This hopefully is the _correct_ way of handling formatting.
		final double v;

		if (type.factor == MathUtilFormatType.TREAT_AS_STANDARD.factor) {
			final String merged = ArrayUtils.StringArrayUtils.addTo(text, text.length - 1, null);

			v = MathUtil.safeParseLargeDoubleWithCommas(merged, -1);
		} else {
			v = MathUtil.safeParseLargeDoubleWithCommasA(text[0], -1);
		}

		if (v == -1 && isNumber(text[0])) {
			return MathUtil.safeParseL(StringsUtil.replace(text[0], ",", ""), -1);
		} else if (v == -1) {
			log.warn("Unknown number: " + Arrays.toString(text));
			log.warn("This should be reported. (v == -1)");
			return -1;
		} else if (type == MathUtilFormatType.UNKNOWN) {
			log.warn("Unknown string: " + Arrays.toString(text));
			log.warn("This should be reported. (type == UNKNOWN)");
			return (long) v;
		} else {
			return (long) (v * type.factor);
		}
	}

	public static void main(String[] args) {
		JVM.println("output: " + format("160 000 wys"));
		JVM.println("output: " + format("4,7 mln"));
		JVM.println("output: " + format("217,976 views"));
		JVM.println("output: " + format("183 subskrybentów"));
		JVM.println("output: " + format("2.9K"));
		JVM.println("output: " + format("1 950 112 wyświetleń"));
	}
}
